/*
 * kl_lib_f4.cpp
 *
 *  Created on: 10.12.2012
 *      Author:     kreyl
 *      Co-author:  roma_jam
 */

#include "main.h"
#include "kl_lib_f4xx.h"
#include <stdarg.h>
#include <string.h>
#include "tiny_sprintf.h"

Delay_t Delay;
uint32_t ITickCounter;
// ============================== Delay ================================= RJ
void Delay_t::Init(void) {
    SysTick_Config(Clk.AHBFreqHz / 8000);   // AHBFreq / SystemCoreClock = 1000 Gz;
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
}
bool Delay_t::Elapsed(uint32_t *AVar, const uint32_t ADelay) {
    if ((ITickCounter - *AVar) >= ADelay) {
        Reset(AVar); // Reset delay
        return true;
    }
    else return false;
}
// IRQ
void SysTick_Handler(void) {
    ITickCounter++;
}

// ============================== UART command =================================
DbgUart_t Uart;
static char UartBuf[198];

void DbgUart_t::Printf(const char *format, ...) {
    va_list args;
    va_start(args, format);
    uint32_t Cnt = tiny_vsprintf(UartBuf, format, args);
    va_end(args);
    if(Cnt > UART_TXBUF_SIZE) Cnt = UART_TXBUF_SIZE;    // Shrink too long string
    if(IDmaIsIdle) {
        memcpy(PTxBuf, UartBuf, Cnt);// Place string to buffer from beginning
        PTxIndex = PTxBuf + Cnt;       // Prepare pointer for next time
        PRxIndex = PTxBuf + Cnt;        // Prepare pointer for next time
        ICountToSendNext = 0;       // Reset next-time counter

        // Start DMA
        IDmaIsIdle = false;
        UART_DMA_TX_STREAM->M0AR = (uint32_t)PTxBuf;      // DMA Memory base adress set
        UART_DMA_TX_STREAM->NDTR = Cnt;                   // DMA Transaction Size
        UART_DMA_TX_STREAM->CR |= (uint32_t)DMA_SxCR_EN;  //DMA Enable
    }
    else {
        ICountToSendNext += Cnt;
        uint32_t BytesFree = UART_TXBUF_SIZE - (PTxIndex - PTxBuf);
        if(Cnt < BytesFree) {   // Message fits in buffer, no splitting needed
            memcpy(PTxIndex, UartBuf, Cnt);
            PTxIndex += Cnt;
        }
        else { // Cnt >= BytesFree
            memcpy(PTxIndex, UartBuf, BytesFree);
            uint32_t Remainder = Cnt - BytesFree;
            if(Remainder) memcpy(PTxBuf, &UartBuf[BytesFree], Remainder);
            PTxIndex = PTxBuf + Remainder;
        }
    } // if not idle
}

// ====================== Init UART, DMA & INTERRUPT ==========================
// Wrapper for IRQ
extern "C" {
    void DMA2_Stream7_IRQHandler(void) {
        if(DMA_GetITStatus(UART_DMA_TX_STREAM, DMA_IT_TCIF7)){ // if transfer completed
            /* Clear DMA Stream Transfer Complete interrupt pending bit */
            DMA_ClearITPendingBit(UART_DMA_TX_STREAM, DMA_IT_TCIF7);
            Uart.IRQDmaTxHandler();   // Handler IRQn
        }
    }
};
void DbgUart_t::Init(uint32_t ABaudrate) {
    PTxBuf = TXBuf1; // Allocate Buffer
    PTxIndex = TXBuf1;

#ifdef RX_ENABLED
    PRxWrite = RXBuf;
    PRxRead = RXBuf;
#endif

    ICountToSendNext = 0;
    IDmaIsIdle = true;

// === USART GPIO Config =====
    PinSetupAlterFunc(GPIOB, 6, omPushPull, pudNone, AF7);      // TX1
#ifdef RX_ENABLED
    PinSetupAlterFunc(GPIOB, 7, omPushPull, pudNone, AF7);      // RX1
#endif
    // ==== USART ====
    rccEnableUSART1(1);     // UART clock, no clock in low-power
    USART1->BRR = Clk.APB2FreqHz / ABaudrate;
    USART1->CR2 = 0;
    USART1->CR3 = USART_CR3_DMAT;   // Enable DMA at transmitter
    USART1->CR1 = USART_CR1_TE;     // Transmitter enabled

    // ==== DMA ====
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);     // Enable DMA clock
    DMA_InitTypeDef  DMA_InitStructure;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single ;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t) (&(USART1->DR)) ;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    /* Configure TX DMA */
    DMA_InitStructure.DMA_Channel = UART_DMA_CHANNEL;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_Init(UART_DMA_TX_STREAM,&DMA_InitStructure); // Need later

    DMA_ITConfig(UART_DMA_TX_STREAM, DMA_IT_TC, ENABLE);   // Enable Transfer complete interrupt

#ifdef RX_ENABLED
    /* Configure RX DMA */
    DMA_InitStructure.DMA_Channel = UART_DMA_CHANNEL;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;
    DMA_InitStructure.DMA_Memory0BaseAddr =(uint32_t)RXBuf;
    DMA_Init(DMA2_Stream2,&DMA_InitStructure);
#endif

    USART1->CR1 |= USART_CR1_UE;        // Enable USART

    // Enable IRQ channel
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


// ==== IRQs ====
void DbgUart_t::IRQDmaTxHandler(){
    UART_DMA_TX_STREAM->CR &= ~(uint32_t)DMA_SxCR_EN;       //DMA Stream must disable to change registers
    if(ICountToSendNext == 0) IDmaIsIdle = true;            // Set point to 0
    else {  // There is something to transmit more
        UART_DMA_TX_STREAM->M0AR = (uint32_t)PRxIndex;      // Set
        // Handle pointer
        uint32_t BytesLeft = UART_TXBUF_SIZE - (PRxIndex - PTxBuf);
        if(ICountToSendNext < BytesLeft) {                  // Data fits in buffer without split
            UART_DMA_TX_STREAM->NDTR = ICountToSendNext;    // DMA Transaction Size
            PRxIndex += ICountToSendNext;
            ICountToSendNext = 0;
        }
        else {  // Some portion of data placed in the beginning
            UART_DMA_TX_STREAM->NDTR = BytesLeft;
            PRxIndex = PTxBuf;  // Set pointer to beginning
            ICountToSendNext -= BytesLeft;
        }
        UART_DMA_TX_STREAM->CR |= (uint32_t)DMA_SxCR_EN;  // Restart DMA
    }
}
